/*
 * Copyright (c) 2011.
 *     This program is free software: you can redistribute it and/or modify
 *     it under the terms of the GNU General Public License as published by
 *     the Free Software Foundation, either version 3 of the License, or
 *     (at your option) any later version.
 *
 *     This program is distributed in the hope that it will be useful,
 *     but WITHOUT ANY WARRANTY; without even the implied warranty of
 *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *     GNU General Public License for more details.
 *
 *     You should have received a copy of the GNU General Public License
 *     along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package org.graphoman.gui;

import org.graphoman.model.Model;
import org.graphoman.model.State;
import org.graphoman.model.Transition;

import java.awt.*;
import java.awt.font.FontRenderContext;
import java.awt.font.TextLayout;
import java.awt.geom.GeneralPath;
import java.awt.image.BufferedImage;

/**
 * Created by IntelliJ IDEA.
 * User: daniil
 * Date: 14.10.11
 * Time: 11:29
 * To change this template use File | Settings | File Templates.
 */
public class Render {
    // graphics
    static Color backgroundColor = Color.decode("0xCCEBFF");
    static Color transitionColor = Color.decode("0xFF6262");
    static Color stateLinesColor = Color.decode("0x0066FF");
    static Color stateBackColor = Color.decode("0xC2E0FF");
    static Color selectedColor = Color.decode("0xFFCC99");
    static Color textColor = Color.decode("0x000066");
    static Font textFont = Font.decode("Ubuntu bold 18");
    static Stroke stroke = new BasicStroke(3.0f);
    public static BufferedImage buffer = new BufferedImage(600, 600, BufferedImage.TYPE_INT_RGB);

    public static void renderModel(Model model, Graphics graphics, Point mouseCurrentCoordinates,
            boolean creatingNewTransition) {
        Graphics2D g = (Graphics2D) buffer.getGraphics();
        g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

        // clearing
        g.setColor(backgroundColor);
        g.fillRect(0, 0, buffer.getWidth(), buffer.getHeight());
        g.scale(model.viewport.scale, model.viewport.scale);
        g.translate(model.viewport.x, model.viewport.y);
        g.setStroke(stroke);
        g.setFont(textFont);

        g.setColor(stateLinesColor);
        g.drawLine(-10, 0, 10, 0);
        g.drawLine(0, 10, 0, -10);

        // transitions

        if (creatingNewTransition && model.createTransitionFrom != null) {
            g.setColor(transitionColor);
            g.drawLine(
                    model.createTransitionFrom.x,
                    model.createTransitionFrom.y,

                    (int) (model.viewport.getModelCoordinates(mouseCurrentCoordinates).x),
                    (int) (model.viewport.getModelCoordinates(mouseCurrentCoordinates).y));

        }
        for (Transition transition : model.transitions) {
            g.setColor(transitionColor);
            if (transition.to != transition.from) {
                // arrow
                GeneralPath path = new GeneralPath();
                // used to draw arrowhead
                Point middle = new Point(
                        transition.from.x + (transition.to.x - transition.from.x) / 2,
                        transition.from.y + (transition.to.y - transition.from.y) / 2
                        );
                Point newControlPoint = new Point(
                        middle.x + (transition.x - middle.x) * 2,
                        middle.y + (transition.y - middle.y) * 2
                        );
                float arrSize = 15;
                float adjSize = (float) (arrSize / Math.sqrt(2));
                //TODO: Comment what is ex?
                Point finishPoint = new Point(
                        (int) (transition.to.x - (transition.to.x - transition.x)
                                / transition.to.distance(transition)
                                * transition.to.diameter / 2),
                        (int) (transition.to.y - (transition.to.y - transition.y)
                                / transition.to.distance(transition)
                                * transition.to.diameter / 2)
                        );
                float ex = transition.to.x - newControlPoint.x;
                float ey = transition.to.y - newControlPoint.y;
                float abs_e = (float) Math.sqrt(ex * ex + ey * ey);
                ex /= abs_e;
                ey /= abs_e;
                // draw arrow
                path.moveTo(
                        transition.from.x,
                        transition.from.y
                        );
                path.quadTo(
                        newControlPoint.x,
                        newControlPoint.y,
                        finishPoint.x,
                        finishPoint.y
                        );
                Point left = new Point(
                        (int) (finishPoint.x + (ey - ex) * adjSize),
                        (int) (finishPoint.y - (ex + ey) * adjSize)
                        );

                Point right = new Point(
                        (int) (finishPoint.x - (ey + ex) * adjSize),
                        (int) (finishPoint.y + (ex - ey) * adjSize)
                        );

                path.lineTo(left.x, left.y);
                path.moveTo(finishPoint.x,
                        finishPoint.y);
                path.lineTo(right.x, right.y);
                g.draw(path);
            } else {
                //loop

                Point stateCoordinates = transition.to;
                Point controlPointCoordinates = transition;
                Point centerOfCircle = new Point(
                        stateCoordinates.x + (controlPointCoordinates.x - stateCoordinates.x) / 2,
                        stateCoordinates.y + (controlPointCoordinates.y - stateCoordinates.y) / 2
                        );
                int diameterOfCircle = (int) stateCoordinates.distance(controlPointCoordinates);
                g.drawOval(
                        centerOfCircle.x - diameterOfCircle / 2,
                        centerOfCircle.y - diameterOfCircle / 2,
                        diameterOfCircle,
                        diameterOfCircle
                        );
            }
            g.setColor(textColor);
            g.drawString(transition.intensity,
                    transition.x,
                    transition.y
                    );

        }

        // states
        for (State state : model.states) {
            if (state == model.selectedState) {
                g.setColor(selectedColor);
            } else {
                g.setColor(state.color);
            }
            g.fillOval(
                    state.x - state.diameter / 2,
                    state.y - state.diameter / 2,
                    state.diameter, state.diameter);
            g.setColor(stateLinesColor);
            g.drawOval(
                    state.x - state.diameter / 2,
                    state.y - state.diameter / 2,
                    state.diameter, state.diameter);
            g.setColor(textColor);

            // try to draw text centered
            FontRenderContext frc = g.getFontRenderContext();
            TextLayout layout = new TextLayout(state.id, textFont, frc);
            layout.draw(g,
                    (float) (-layout.getBounds().getCenterX() + state.x),
                    (float) (-layout.getBounds().getCenterY() + state.y));

        }
        graphics.drawImage(buffer, 0, 0, null);
    }

}
